跳到主要内容

数据绑定

在使用运行时数据绑定 API 之前,请先熟悉概述中介绍的核心概念。

视图模型

视图模型描述一组属性,但不能直接用于获取或设置值——这是视图模型实例的职责。

首先,我们需要获取对特定视图模型的引用。可以通过索引、名称或给定画板的默认值从 Rive 文件中获取。默认选项指的是编辑器中下拉菜单为画板分配的视图模型。

从创建的 Rive 对象在 onLoad 回调中访问视图模型:

const r = new rive.Rive({
onLoad: () => {
// Rive 对象现在已加载并可以使用。
}
});

Rive 加载后,你可以使用以下方法访问视图模型:

// 按名称获取引用
const namedVM = r.viewModelByName("My View Model");

// 按索引获取引用
for (let i = 0; i < r.viewModelCount; i++) {
const indexedVM = r.viewModelByIndex(i);
}

// 获取默认视图模型的引用
const defaultVM = r.defaultViewModel();

或者,如果你可以访问底层的 RiveFile 对象,可以在文件上访问上述方法。

const file = new RiveFile(/** ... */);
await file.init();
const vmFromFile = file.viewModelByName("My View Model");

// 或通过其他方式从 RiveFile 底层实例获取 ViewModel
const fileInstance = file.getInstance();
const namedVM = fileInstance.viewModelByName("My View Model");
const indexedVM = fileInstance.viewModelByIndex(0);
const defaultVM = fileInstance.defaultArtboardViewModel(artboard);

视图模型实例

一旦我们有了视图模型的引用,就可以用它来创建实例。创建实例时有四个选项:

  1. 创建空白实例 —— 使用以下默认值填充创建的实例的属性:

    类型
    Number0
    String空字符串
    BooleanFalse
    Color0xFF000000
    Trigger未触发
    Enum第一个值
    Image无图片
    Artboard无画板
    List空列表
    嵌套视图模型Null
  2. 创建默认实例 —— 使用编辑器中标记为「Default」的实例。通常这是设计师打算在运行时使用的主要实例。

  3. 按索引创建 —— 使用迭代所有可用实例时返回的顺序。在通过迭代创建多个实例时很有用。

  4. 按名称创建 —— 使用编辑器中的实例名称。在创建特定实例时很有用。

在某些示例中,由于「view model instance」比较冗长,我们使用缩写「VMI」,以及「VM」代表「view model」。

// 从视图模型创建空白实例 (ViewModel)
const vmiBlank = viewModel.instance();

// 从视图模型创建默认实例 (ViewModel)
const vmiDefault = viewModel.defaultInstance();

// 从视图模型按索引创建实例 (ViewModel)
for (let i = 0; i < viewModel.instanceCount; i++) {
const vmiIndexed = viewModel.instanceByIndex(i);
}

// 从视图模型按名称创建实例 (ViewModel)
const vmiNamed = viewModel.instanceByName("My Instance");

绑定

然后可以将创建的实例分配给状态机或画板。这将建立在编辑时设置的绑定关系。

最好分配给状态机,因为这将自动将实例也应用于画板。仅在不使用状态机的情况下(即文件是静态的或使用线性动画)才分配给画板。

实例的初始值不会应用于其绑定元素,直到状态机或画板推进。

const r = new rive.Rive({
autoBind: false, // 应设置为 false(默认值)
onLoad: () => {
const vm = r.viewModelByName("My View Model");
const vmi = vm.instanceByName("My Instance");

// 通过将实例应用于状态机和画板来手动绑定
r.bindViewModelInstance(vmi);
}
});

自动绑定

或者,你可能会更倾向于使用自动绑定。这将自动使用默认实例将画板的默认视图模型绑定到状态机和画板。默认视图模型是在编辑器下拉菜单中为画板选择的视图模型。默认实例是编辑器中标记为「Default」的实例。

const r = new rive.Rive({
src: "my_rive_file.riv",
canvas: document.getElementById("canvas"),
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let boundInstance = r.viewModelInstance;
}
});

从实例获取视图模型

使用视图模型实例时,你可能希望获取实例来源视图模型的引用,以便动态创建更多相同类型的实例(例如从列表中创建实例)。为此,首先通过 .viewModelName 属性获取实例来源的视图模型名称:

const vmi = r.viewModelInstance;
const vmName = vmi.viewModelName;

然后,一旦有了名称,你可以获取视图模型本身的引用并按需创建实例。

const vmi = r.viewModelInstance;
const mainListProp = vmi.list("todos") as ViewModelInstanceList;
const vmName = mainListProp.instanceAt(0)?.viewModelName;

const itemVmReference = r.viewModelByName(vmName);
const instanceCopies = [];
for (let i = 0; i < 10; i++) {
instanceCopies.push(itemVmReference.defaultInstance());
}

属性

属性是可以在视图模型实例上读取、设置或观察的值。属性可以是以下类型:

类型支持
浮点数
布尔值
触发器
字符串
枚举
颜色
嵌套视图模型
列表
图片
画板

关于版本兼容性,请参见功能支持页面。

列出属性

可以在视图模型上检查属性描述符,以在运行时发现可用的属性。但这些不是可变的属性本身——属性依然在实例上。这些描述符有类型和名称。

    // 视图模型上的属性列表 (ViewModel)
const properties = viewModel.properties;
console.log(properties);

读取和写入属性

可以通过名称或路径检索对这些属性的引用。

某些属性是可变的,并对其值有 getter、setter 和观察者操作。获取或观察值将检索自上次状态机或画板推进以来,对该属性绑定设置的最新值。设置值将更新值和其所有绑定元素。

设置属性值后,更改不会应用于其绑定元素,直到状态机或画板推进。

const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;

// 布尔值
const booleanProperty = vmi.boolean("My Boolean Property");
const booleanValue = booleanProperty.value;
booleanProperty.value = true;

// 字符串
const stringProperty = vmi.string("My String Property");
const stringValue = stringProperty.value;
stringProperty.value = "Hello, Rive!";

// 数字
const numberProperty = vmi.number("My Number Property");
const numberValue = numberProperty.value;
numberProperty.value = 10;

// 颜色
const colorProperty = vmi.color("My Color Property");
const colorValue = colorProperty.value;
colorProperty.value = 0xFF000000; // 设置颜色为黑色,100% 不透明度

// 设置颜色的其他方式
colorProperty.rgb(255, 0, 0); // 设置 RGB 为红色
colorProperty.rgba(255, 0, 0, 128); // 设置 RGBA 为红色,50% 不透明度
colorProperty.argb(128, 255, 0, 0); // 设置 ARGB 为红色,50% 不透明度
colorProperty.opacity(0.5); // 设置不透明度为 50%

// 触发器
const triggerProperty = vmi.trigger("My Trigger Property");
triggerProperty.trigger();

// 枚举
const enumProperty = vmi.enum("My Enum Property");
const enumValue = enumProperty.value;
enumProperty.value = "Option1";
}
});

嵌套属性路径

视图模型可以有视图模型类型的属性,允许任意嵌套。你可以在每个实例上从根开始链式调用属性,直到获取到感兴趣的属性。或者,你可以通过路径参数来做到这一点,类似于 URI,以正斜杠分隔的属性名称列表,以感兴趣的属性名称结尾。

const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;

const nestedNumberByChain = vmi
.viewModel("My Nested View Model")
.viewModel("My Second Nested VM")
.number("My Nested Number");

const nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number");
}
});

可观察性

你可以通过使用监听器或平台等效方法观察属性值随时间的变化。一旦开始观察,当状态机推进应用属性更改时,你将收到通知,无论是显式设置的新值还是绑定更新后的值。

为属性添加观察者通过调用属性上的 on 方法完成。

public on(callback: EventCallback)

观察者可以通过调用属性上的 off 方法并传入回调函数来移除。或者,你可以不带参数调用 off() 来移除所有观察者。

public off(callback?: EventCallback)

示例:

const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;
const numberProperty = vmi.number("My Number Property");
// 观察
numberProperty.on((event) => {
console.log(event.data);
});
// 完成后移除所有监听器
numberProperty.off();
}
});

图片

图片属性允许你在运行时设置和替换位图图像,每个图像实例独立管理。例如,你可以构建一个头像创建器,并通过设置视图模型的图像属性动态更新特征——比如换一顶帽子。

const randomImageAsset = (imageProperty) => {
fetch("https://picsum.photos/300/500").then(async (res) => {
// 从响应中解码图像。此对象用于设置图像属性。
const image = await rive.decodeImage(
new Uint8Array(await res.arrayBuffer())
);
imageProperty.value = image;
// Rive 会自动清理此项。但最好在设置解码图像后手动释放。
// 如果你打算再次使用解码的资源,请不要调用 `unref`。
image.unref();
});
};

const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;

// 按名称获取图像属性
var imageProperty = vmi.image("bound_image");

// 加载随机图像
randomImageAsset(imageProperty);

// 清除图像以渲染空白
imageProperty.value = null;
}
});

列表

列表属性允许你在运行时管理一组动态的视图模型实例。例如,你可以构建一个 TODO 应用,用户可以在可滚动的布局中添加和删除任务。

请参阅编辑器部分关于创建数据绑定列表的内容。

单个列表属性可以包含不同的视图模型类型,每个视图模型与其自己的组件绑定,从而轻松用各种组件实例填充列表。

使用列表属性,你可以:

  • 添加新的视图模型实例(可选按索引)
  • 删除现有的视图模型实例(可选按索引)
  • 按索引交换两个视图模型实例
  • 获取列表的大小

关于列表属性的更多信息,请参阅数据绑定列表属性编辑器文档。

const r = new rive.Rive({
autoBind: true,
onLoad: () => {
// 访问已自动绑定的当前实例
let vmi = r.viewModelInstance;

// 按名称获取列表属性
var list = vmi.list("todos");
console.log("length: ", list.length);

// 获取视图模型
var todoItemVM = r.viewModelByName("TodoItem");

// 从视图模型创建空白实例。
// 为要添加的每个新项目执行此操作。
var myTodo = todoItemVM.instance();
myTodo.string("description").value = "Buy groceries";

// 将新创建的实例添加到列表中
list.addInstance(myTodo);

// 从列表中删除特定实例
list.removeInstance(myTodo);

// 交换列表中索引 0 和 1 的两个实例
list.swap(0, 1);

// 删除索引 0 处的实例
list.removeInstanceAt(0);
}
});

画板

画板属性允许你在运行时替换整个组件。这对于创建可以在不同设计或应用间重用的模块化组件非常有用,例如:

  • 创建支持大量变体的皮肤系统,如角色创建器,可以替换不同的身体部位、服装和配饰。
  • 创建一个复杂场景,由从各种不同 Rive 文件加载的多个画板组成(绘制到单个 canvas/纹理/控件上)。
  • 通过将单个 Rive 文件分解为更小的组件来减小其大小(复杂度),这些组件可以按需加载并按需替换。
let artboardProperty = null;
let characterArtboard = null;

function attachCharacter() {
// 如果画板属性和角色画板都存在,则设置画板
if (characterArtboard && artboardProperty) {
artboardProperty.value = characterArtboard;
}
}

const r = new Rive({
src: "swap_character_main.riv",
autoplay: true,
canvas: el,
autoBind: true,
layout: new Layout({
fit: Fit.Layout,
layoutScaleFactor: 0.5,
}),
stateMachines: "State Machine 1",
onLoad: () => {
r.resizeDrawingSurfaceToCanvas();

const vmi = r.viewModelInstance;
artboardProperty = vmi.artboard("Artboard property");

attachCharacter();
},
onLoadError: () => {
console.log("error");
},
});

// 加载外部画板
const assetsFile = new RiveFile({
src: "swap_character_assets.riv",
onLoad: () => {
characterArtboard = assetsFile.getBindableArtboard("Character 1");
attachCharacter();
},
onLoadError: () => {
console.log("error");
},
});
assetsFile.init();

在上面的示例中,我们从单独的 swap_character_assets.riv 文件加载以获取 Character 1 可绑定画板。然后我们可以通过将 artboardProperty 的值设置为加载的 characterArtboard 来在主 swap_character_main.riv 文件中使用此值。这允许我们在运行时替换主文件中使用的画板。此外,你可以通过 .viewModel setter 为该可绑定画板设置外部的 ViewModelInstance

枚举

枚举属性分为两种:系统枚举和用户自定义枚举。实际上,你不需要担心这种区别,只需知道系统枚举在任何绑定到编辑器定义枚举集的 Rive 文件中都可用,代表编辑器下拉菜单中的选项,而用户自定义枚举是由设计师在编辑器中定义的。

枚举是字符串类型。Rive 文件包含枚举列表。每个枚举又有一个名称和一个字符串列表。

const r = new rive.Rive({
onLoad: () => {
const enums = r.enums();

console.log(enums);
}
});

示例

视频教程: